#include "UEdit.h"
#include "UControl.h"
#include <boost/algorithm/string.hpp>
#include "../UGLFontObj.h"

using namespace std;

#define LeftSpacing 3
#define TopSpacing 3

string CharToString(char C)
{
	string result;
	result.push_back(C);
	return result;
}

TEdit::TEdit(TRectangle rect)
: TControl(rect)
{
	textcolor = TVector(1,1,1);
	Position = (SelPos = 0);
	CursorTimer = 0;
	clearOnEnter = false;

	KeyPressed = SDLK_UNKNOWN;
	FrameCountLastPressed = 0;
	HorScroll = 0;
	FontObj = NULL;
	MState = false;
}

void TEdit::SetText(string text)
{
	Text = text;
	Position = (SelPos = 0);
}

string TEdit::GetText()
{
	return Text;
}

size_t TEdit::CharIndexAtX(int X)
{
	int LR = X-Left-LeftSpacing+HorScroll;
	int Length= 0;
	size_t i = 0;

	for (; i < Text.size() && Length<LR-Font->getCharWidth(Text[i])/2; ++i)
	{
		Length = Length + Font->getCharWidth(Text[i]);
	}
	return i;
}

void TEdit::DeleteSelection()
{
	if (SelPos!=Position && Text.size()!=0)
	{
		Text.erase(min(SelPos, Position), abs(SelPos-Position));
		Position = (SelPos = min(SelPos, Position));
	}
}

void TEdit::CopySelection()
{
	ClipBoard = boost::trim_copy(Text.substr( min(SelPos, Position), abs(SelPos-Position)));
}

void TEdit::MouseDown(int X, int Y, uint8 button)
{
	TControl::MouseDown(X, Y, button);
	Position = (SelPos = CharIndexAtX(X));
	CursorTimer = 0;
	ReCalculateAll();
	MState = true;

}

void TEdit::MouseUp(int X, int Y, uint8 button)
{
	MState = false;
	TControl::MouseUp(X, Y, button);
}

void TEdit::MouseMove(int X, int Y, int mstate)
{
	MState = (mstate==1);
	if (mstate)
	{
		LastX = X;
		Position = CharIndexAtX(X);
	}

	Length2 = 0;
	for (int i=0; i < Position; ++i)
		Length2 = Length2 + Font->getCharWidth(Text[i]);

}

void TEdit::Draw( )
{

	glColor3f(0.1, 0.1, 0.1);
	glBegin(GL_QUADS);
		glVertex2i(Left		, Top);
		glVertex2i(Right	, Top);
		glVertex2i(Right	, Bottom);
		glVertex2i(Left		, Bottom);
	glEnd();

	glColor3f(0.7, 0.75, 0.75);
	glBegin(GL_QUADS);
		glVertex2i(Left+1	, Top+1);
		glVertex2i(Right-1	, Top+1);
		glVertex2i(Right-1	, Bottom-1);
		glVertex2i(Left+1	, Bottom-1);
	glEnd();

	int L  = max(Left, min(Left+Length +LeftSpacing-HorScroll, Right));
	int L2 = max(Left, min(Left+Length2+LeftSpacing-HorScroll, Right));
	if (SelPos>-1 && SelPos!=Position)
	{
		glColor3f(0, 0, 1);
		glBegin(GL_QUADS);
			glVertex2i(L, Top+TopSpacing);
			glVertex2i(L, Top+TopSpacing+Font->Height());

			glVertex2i(L2, Top+TopSpacing+Font->Height());
			glVertex2i(L2, Top+TopSpacing);

		glEnd();
	}

	// calibrated for 60fps, leave like that please
	if (IsFocussed() && CursorTimer++&32)
	{	glColor3f(1,1,1);
		glBegin(GL_LINE_STRIP);
			glVertex2i(L2, Top+TopSpacing);
			glVertex2i(L2, Top+TopSpacing+Font->Height());
		glEnd();
	}

	if ( (KeyPressed!=SDLK_UNKNOWN) && (FrameCountLastPressed++ > 40)&& (FrameCountLastPressed%3==0) )
	{
		SDL_KeyboardEvent* Key = new SDL_KeyboardEvent();
		Key->keysym.sym = KeyPressed;
		KeyDown(Key);
		delete Key;
	}

	//Drag if selecting text out of edit;
	if ( (LastX<Left)&&(MState))
	{
		HorScroll = max(HorScroll-1, 0);
		ReCalculateAll();
	} else if ((LastX>Right)&&(MState))
	{
		HorScroll = HorScroll+1;
		ReCalculateAll();
	}
	// calibrated for 60fps, leave like that please

	glColor3fv(&textcolor.x);

	if (FontObj != NULL)
		FontObj->Draw();


}

void TEdit::ReCalculateAll()
{
	Length = 0;
	for (int i=0; i < SelPos; ++i)
		Length = Length + Font->getCharWidth(Text[i]);

	Length2 = 0;
	for (int i=0; i < Position; ++i)
	{
		Length2 = Length2 + Font->getCharWidth(Text[i]);
	}

	int TextWidth = Length2;
	for (int i=Position; i < Text.size(); ++i)
	{
		TextWidth = TextWidth + Font->getCharWidth(Text[i]);
	}

	//Scroll text when cursor beyond a edge of the edit
	if (TextWidth> Right-Left-LeftSpacing-LeftSpacing)
	{

		if (Left+Length2+LeftSpacing-HorScroll>Right-LeftSpacing)
		{	//Scroll to the right
			HorScroll = (Left+Length2+LeftSpacing)-(Right-LeftSpacing);
		}
		else if (Left+Length2+LeftSpacing-HorScroll<Left+LeftSpacing)
		{	//Scroll to the left
			HorScroll+= (Left+Length2+LeftSpacing-HorScroll)-(Left+LeftSpacing);
		}

		if (Left+TextWidth+LeftSpacing-HorScroll<Right-LeftSpacing)
		{	//Dont scroll more then needed
			HorScroll = (Left+TextWidth+LeftSpacing)-(Right-LeftSpacing);
		}

	}
	else
	{	//Don't scroll at all
		HorScroll = 0;
	}

	if (FontObj != NULL)
		FontObj->~TGLFontObj();
	FontObj = new TGLFontObj(Font, TRectangle(Left+LeftSpacing-HorScroll, Top+TopSpacing, Right, Bottom-TopSpacing), TP_Left, TP_VCenter, Text.c_str());


}

void TEdit::KeyDown(SDL_KeyboardEvent* Key)
{
	if (KeyPressed!=Key->keysym.sym)
	{
		KeyPressed = Key->keysym.sym;
		FrameCountLastPressed = 0;
	}

	if (KeyPressed == SDLK_RETURN)
	{
		if (onEnter) onEnter();
		if (clearOnEnter) {
			Text.clear();
			Position = (SelPos = 0);
		}
		return;
	}

	if (Key->keysym.mod & KMOD_CTRL)
	{
		char Char = KeyPressed;
		switch (Char) {
			case 'x'  : CopySelection();
						DeleteSelection(); break;
			case 'c'  : CopySelection(); break;
			case 'v'  : DeleteSelection();
						Text.insert(Position, ClipBoard);
						Position = (SelPos = (Position+ClipBoard.size()));
						break;

		}
		ReCalculateAll();
		return;

	}

	bool Change = true;

	switch (KeyPressed) {
		case SDLK_BACKSPACE:
							if (Position>0 && SelPos==Position) //selected text will be romoved later
								Text.erase(--Position, 1);

							break;
		case SDLK_LEFT:		Position = max(0, Position -1);
							if (!(Key->keysym.mod & KMOD_SHIFT))
								SelPos = Position;
							Change = false; break;
		case SDLK_RIGHT:	Position = min((int)Text.length(), Position+1);
							if (!(Key->keysym.mod & KMOD_SHIFT))
								SelPos = Position;
							Change = false;  break;
		case SDLK_DELETE:	if (Position<Text.length()&& SelPos==Position)//selected text will be romoved later
								Text.erase(Position, 1);
							break;
		case SDLK_RSHIFT:
		case SDLK_LSHIFT:	Change = false; break;
		case SDLK_LCTRL:
		case SDLK_RCTRL:	Change = false; break;
		case SDLK_CAPSLOCK:
		case SDLK_TAB:		Change = false; break;
		case SDLK_LALT:
		case SDLK_RALT:		Change = false; break;
		case SDLK_HOME:		Position = 0;
							if (!(Key->keysym.mod & KMOD_SHIFT))
								SelPos = Position;
							Change = false; break;
		case SDLK_END:		Position = Text.length();
							if (!(Key->keysym.mod & KMOD_SHIFT))
								SelPos = Position;
							Change = false; break;

		default:
		{
			char Char = KeyPressed;
			if (Key->keysym.mod & KMOD_CAPS)
				Char = toupper(Char);



			if (Key->keysym.mod & KMOD_SHIFT)
			{

				if ( (Char>='a') && (Char<='z'))
					Char = toupper(Char);

				if (Key->keysym.mod & KMOD_CAPS)
					Char = tolower(Char);

				else
					switch (Char) {
						case '/'  :Char = '?'; break;
						case ';'  :Char = ':'; break;
						case '\'' :Char = '"'; break;
						case '['  :Char = '{'; break;
						case ']'  :Char = '}'; break;
						case '\\' :Char = '|'; break;
						case '1'  :Char = '!'; break;
						case '2'  :Char = '@'; break;
						case '3'  :Char = '#'; break;
						case '4'  :Char = '$'; break;
						case '5'  :Char = '%'; break;
						case '6'  :Char = '^'; break;
						case '7'  :Char = '&'; break;
						case '8'  :Char = '*'; break;
						case '9'  :Char = '('; break;
						case '0'  :Char = ')'; break;
						case '-'  :Char = '_'; break;
						case '='  :Char = '+'; break;
						case '`'  :Char = '~'; break;
					}

			}


			DeleteSelection();
			Text.insert(Position, CharToString( Char));
			SelPos = (++Position);
		};

	};


	if (Change)
	{
		DeleteSelection();
		if (onChange) onChange();

	}
	ReCalculateAll();
}

void TEdit::KeyUp(SDL_KeyboardEvent* Key)
{
	KeyPressed = SDLK_UNKNOWN;
}
